module system_core #(
  parameter UART_CLK_DIV = 0
)(
        input           i_clk,
        input           i_rst,

        output          o_uart_cts,
        input           i_uart_rts,
        output          o_uart_rx,
        input           i_uart_tx,

        input   [ 3:0]  i_gpio_sw,

        output          o_ddr3_instr_read,
        output  [31:2]  o_ddr3_instr_addr,
        input           i_ddr3_instr_ack,
        input   [31:0]  i_ddr3_instr_data,

        output          o_ddr3_wb_cyc,
        output          o_ddr3_wb_stb,
        output          o_ddr3_wb_we,
        output  [ 2:0]  o_ddr3_wb_cti,
        output  [ 1:0]  o_ddr3_wb_bte,
        output  [30:2]  o_ddr3_wb_adr,
        output  [ 3:0]  o_ddr3_wb_sel,
        output  [31:0]  o_ddr3_wb_dat,

        input           i_ddr3_wb_ack,
        input   [31:0]  i_ddr3_wb_dat
);

localparam INTERNAL_RAM_WIDTH = 12;

// ======================================
// Wishbone Buses
// ======================================

// 我们有多个Master设备，故需要仲裁

localparam WB_DWIDTH  = 32;
localparam WB_SWIDTH  =  4; // SEL信号宽度

localparam WB_M0AWIDTH = 30; // Ethernet MAC
localparam WB_M1AWIDTH = 30; // CPU
localparam WB_S0AWIDTH = INTERNAL_RAM_WIDTH-2;
localparam WB_S1AWIDTH =  1; // Simple UART (1 word)
localparam WB_S2AWIDTH =  1; // Timer (2 words)
localparam WB_S3AWIDTH = 10; // Ethernet MAC
localparam WB_S4AWIDTH =  1; // Pushbuttons
localparam WB_S5AWIDTH = 29; // DDR3 SDRAM
localparam WB_S6AWIDTH =  1; // Simulation controller (2 words)
localparam WB_S7AWIDTH = 30; // (unused)

// Wishbone Master Buses

wire                        m0o_wb_cyc;
wire                        m0o_wb_stb;
wire                        m0o_wb_we ;
wire      [2:0]             m0o_wb_cti;
wire      [1:0]             m0o_wb_bte;
wire      [WB_M0AWIDTH+1:2] m0o_wb_adr;
wire      [WB_SWIDTH-1:0]   m0o_wb_sel;
wire      [WB_DWIDTH-1:0]   m0o_wb_dat;
wire                        m0i_wb_ack;
wire      [WB_DWIDTH-1:0]   m0i_wb_dat;

wire                        m1o_wb_cyc;
wire                        m1o_wb_stb;
wire                        m1o_wb_we ;
wire      [2:0]             m1o_wb_cti;
wire      [1:0]             m1o_wb_bte;
wire      [WB_M1AWIDTH+1:2] m1o_wb_adr;
wire      [WB_SWIDTH-1:0]   m1o_wb_sel;
wire      [WB_DWIDTH-1:0]   m1o_wb_dat;
wire                        m1i_wb_ack;
wire      [WB_DWIDTH-1:0]   m1i_wb_dat;

// Wishbone Slave Buses

wire                        s0i_wb_cyc;
wire                        s0i_wb_stb;
wire                        s0i_wb_we ;
wire      [2:0]             s0i_wb_cti;
wire      [1:0]             s0i_wb_bte;
wire      [WB_S0AWIDTH+1:2] s0i_wb_adr;
wire      [WB_SWIDTH-1:0]   s0i_wb_sel;
wire      [WB_DWIDTH-1:0]   s0i_wb_dat;
wire                        s0o_wb_ack;
wire      [WB_DWIDTH-1:0]   s0o_wb_dat;

wire                        s1i_wb_cyc;
wire                        s1i_wb_stb;
wire                        s1i_wb_we ;
wire      [2:0]             s1i_wb_cti;
wire      [1:0]             s1i_wb_bte;
wire      [WB_S1AWIDTH+1:2] s1i_wb_adr;
wire      [WB_SWIDTH-1:0]   s1i_wb_sel;
wire      [WB_DWIDTH-1:0]   s1i_wb_dat;
wire                        s1o_wb_ack;
wire      [WB_DWIDTH-1:0]   s1o_wb_dat;

wire                        s2i_wb_cyc;
wire                        s2i_wb_stb;
wire                        s2i_wb_we ;
wire      [2:0]             s2i_wb_cti;
wire      [1:0]             s2i_wb_bte;
wire      [WB_S2AWIDTH+1:2] s2i_wb_adr;
wire      [WB_SWIDTH-1:0]   s2i_wb_sel;
wire      [WB_DWIDTH-1:0]   s2i_wb_dat;
wire                        s2o_wb_ack;
wire      [WB_DWIDTH-1:0]   s2o_wb_dat;

wire                        s3i_wb_cyc;
wire                        s3i_wb_stb;
wire                        s3i_wb_we ;
wire      [2:0]             s3i_wb_cti;
wire      [1:0]             s3i_wb_bte;
wire      [WB_S3AWIDTH+1:2] s3i_wb_adr;
wire      [WB_SWIDTH-1:0]   s3i_wb_sel;
wire      [WB_DWIDTH-1:0]   s3i_wb_dat;
wire                        s3o_wb_ack;
wire      [WB_DWIDTH-1:0]   s3o_wb_dat;

wire                        s4i_wb_cyc;
wire                        s4i_wb_stb;
wire                        s4i_wb_we ;
wire      [2:0]             s4i_wb_cti;
wire      [1:0]             s4i_wb_bte;
wire      [WB_S4AWIDTH+1:2] s4i_wb_adr;
wire      [WB_SWIDTH-1:0]   s4i_wb_sel;
wire      [WB_DWIDTH-1:0]   s4i_wb_dat;
wire                        s4o_wb_ack;
wire      [WB_DWIDTH-1:0]   s4o_wb_dat;

wire                        s5i_wb_cyc;
wire                        s5i_wb_stb;
wire                        s5i_wb_we ;
wire      [2:0]             s5i_wb_cti;
wire      [1:0]             s5i_wb_bte;
wire      [WB_S5AWIDTH+1:2] s5i_wb_adr;
wire      [WB_SWIDTH-1:0]   s5i_wb_sel;
wire      [WB_DWIDTH-1:0]   s5i_wb_dat;
wire                        s5o_wb_ack;
wire      [WB_DWIDTH-1:0]   s5o_wb_dat;

wire                        s6i_wb_cyc;
wire                        s6i_wb_stb;
wire                        s6i_wb_we ;
wire      [2:0]             s6i_wb_cti;
wire      [1:0]             s6i_wb_bte;
wire      [WB_S6AWIDTH+1:2] s6i_wb_adr;
wire      [WB_SWIDTH-1:0]   s6i_wb_sel;
wire      [WB_DWIDTH-1:0]   s6i_wb_dat;
wire                        s6o_wb_ack;
wire      [WB_DWIDTH-1:0]   s6o_wb_dat;

wire                        s7i_wb_cyc;
wire                        s7i_wb_stb;
wire                        s7i_wb_we ;
wire      [2:0]             s7i_wb_cti;
wire      [1:0]             s7i_wb_bte;
wire      [WB_S7AWIDTH+1:2] s7i_wb_adr;
wire      [WB_SWIDTH-1:0]   s7i_wb_sel;
wire      [WB_DWIDTH-1:0]   s7i_wb_dat;
wire                        s7o_wb_ack;
wire      [WB_DWIDTH-1:0]   s7o_wb_dat;

// peripheral IRQs
wire	uart_irq;
wire	timer_irq;

wire            cpu_instr_read;
wire	[31:2]	cpu_pc;
wire	[31:0]	bootram_instr_data;
wire	        cpu_irq;

wire bootram_instr_read;

reg cpu_instr_ack;
assign uart_irq = 1'b0;
assign cpu_irq = uart_irq | timer_irq ;

wire instr_sel_bootram;
wire instr_sel_ddr3;

// FIXME hard-coded memory configuration
assign instr_sel_bootram = {cpu_pc[31:16], 16'b0} == 32'h00000000;
assign instr_sel_ddr3 = {cpu_pc[31:30], 30'b0} == 32'h80000000;

assign bootram_instr_read = cpu_instr_read & instr_sel_bootram;
assign o_ddr3_instr_read = cpu_instr_read & instr_sel_ddr3;
assign o_ddr3_instr_addr = cpu_pc;

reg [1:0] instr_sel_r;
reg [31:0] cpu_instr_data;

always @* begin
  case (1'b1)
    instr_sel_r[0]: begin
      cpu_instr_ack = 1'b1;
      cpu_instr_data = bootram_instr_data;
    end
    instr_sel_r[1]: begin
      cpu_instr_ack = i_ddr3_instr_ack;
      cpu_instr_data = i_ddr3_instr_data;
    end
    default: begin
      cpu_instr_ack = 1'b0;
      cpu_instr_data = 32'bx;
    end
  endcase
end

always @(posedge i_clk) begin
  if (cpu_instr_read) begin
    instr_sel_r[0] <= instr_sel_bootram;
    instr_sel_r[1] <= instr_sel_ddr3;
  end
end

// CPU
`ifdef USE_EMULATED_CPU
cpu_emulated
`else
cpu
`endif
u_cpu(
	.i_wb_clk(i_clk),
	.i_wb_rst(i_rst),
	.i_wb_dat(m1i_wb_dat),
	.i_wb_ack(m1i_wb_ack),
	.o_wb_dat(m1o_wb_dat),
	.o_wb_adr(m1o_wb_adr),
	.o_wb_sel(m1o_wb_sel),
	.o_wb_cyc(m1o_wb_cyc),
	.o_wb_stb(m1o_wb_stb),
	.o_wb_cti(m1o_wb_cti),
	.o_wb_bte(m1o_wb_bte),
	.o_wb_we (m1o_wb_we ),
	.i_irq(cpu_irq),
	//.i_fiq(cpu_fiq),
	.o_pc(cpu_pc),
        .o_read_instr(cpu_instr_read),
        .i_instr_ack(cpu_instr_ack),
	.i_instr(cpu_instr_data)
);

// Boot RAM
ram #(INTERNAL_RAM_WIDTH) u_ram(
	.CLK_I(i_clk),
	.RST_I(i_rst),
	.CYC_I(s0i_wb_cyc),
	.STB_I(s0i_wb_stb),
	.WE_I (s0i_wb_we ),
	.ADR_I(s0i_wb_adr),
	.SEL_I(s0i_wb_sel),
	.DAT_I(s0i_wb_dat),
	.DAT_O(s0o_wb_dat),
	.ACK_O(s0o_wb_ack),
        .i_read_instr(bootram_instr_read),
	.i_addr_instr(cpu_pc[INTERNAL_RAM_WIDTH-1:2]),
	.o_data_instr(bootram_instr_data)
);

// UART
/*uart_top u_uart_top(
	.wb_clk_i(i_clk),
	.wb_rst_i(i_rst),
	.wb_adr_i(s1i_wb_adr),
	.wb_dat_i(s1i_wb_dat),
	.wb_dat_o(s1o_wb_dat),
	.wb_we_i (s1i_wb_we ),
	.wb_stb_i(s1i_wb_stb),
	.wb_cyc_i(s1i_wb_cyc),
	.wb_ack_o(s1o_wb_ack),
	.wb_sel_i(s1i_wb_sel),
	.int_o(uart_irq),
	.stx_pad_o(o_uart_rx),
	.srx_pad_i(i_uart_tx),
	.rts_pad_o(o_uart_cts),
	.cts_pad_i(i_uart_rts),
	.dtr_pad_o(),
	.dsr_pad_i(1'b0),
	.ri_pad_i(1'b0),
	.dcd_pad_i(1'b0)
);*/

assign o_uart_cts = 1'b0;
simple_uart #(UART_CLK_DIV) u_simple_uart(
  .i_wb_clk(i_clk),
  .i_wb_rst(i_rst),
  .i_wb_cyc(s1i_wb_cyc),
  .i_wb_stb(s1i_wb_stb),
  .i_wb_we (s1i_wb_we ),
  .i_wb_sel(s1i_wb_sel),
  .i_wb_dat(s1i_wb_dat),
  .o_wb_ack(s1o_wb_ack),
  .o_wb_dat(s1o_wb_dat),
  .o_txd(o_uart_rx)
);

// 计时器
timer u_timer(
	.CLK_I(i_clk),
	.RST_I(i_rst),
	.CYC_I(s2i_wb_cyc),
	.STB_I(s2i_wb_stb),
	. WE_I(s2i_wb_we ),
	.ADR_I(s2i_wb_adr),
	.DAT_I(s2i_wb_dat),
	.SEL_I(s2i_wb_sel),
	.ACK_O(s2o_wb_ack),
	.DAT_O(s2o_wb_dat),
	.o_irq(timer_irq)
);

// 按钮
switch u_switch(
        .CLK_I(i_clk),
        .RST_I(i_rst),
	.CYC_I(s4i_wb_cyc),
	.STB_I(s4i_wb_stb),
	.ACK_O(s4o_wb_ack),
	.DAT_O(s4o_wb_dat),
        .i_sw0(i_gpio_sw[0]),
        .i_sw1(i_gpio_sw[1]),
        .i_sw2(i_gpio_sw[2]),
        .i_sw3(i_gpio_sw[3])
);

simctl u_simctl(
        .i_wb_clk(i_clk),
        .i_wb_rst(i_rst),
	.i_wb_cyc(s6i_wb_cyc),
	.i_wb_stb(s6i_wb_stb),
	.i_wb_we (s6i_wb_we ),
	.i_wb_adr(s6i_wb_adr),
	.i_wb_dat(s6i_wb_dat),
	.i_wb_sel(s6i_wb_sel),
	.o_wb_ack(s6o_wb_ack),
	.o_wb_dat(s6o_wb_dat)
);

assign m0o_wb_adr = 0;
assign m0o_wb_sel = 0;
assign m0o_wb_we  = 0;
assign m0o_wb_dat = 0;
assign m0o_wb_cyc = 0;
assign m0o_wb_stb = 0;

`ifdef ENABLE_ETHMAC
//TODO
assign s3o_wb_ack = i_eth_wb_ack;
assign s3o_wb_dat = i_eth_wb_dat;
`else
assign s3o_wb_ack = 0;
assign s3o_wb_dat = 0;
`endif

assign o_ddr3_wb_cyc = s5i_wb_cyc;
assign o_ddr3_wb_stb = s5i_wb_stb;
assign o_ddr3_wb_we  = s5i_wb_we ;
assign o_ddr3_wb_cti = s5i_wb_cti;
assign o_ddr3_wb_bte = s5i_wb_bte;
assign o_ddr3_wb_adr = s5i_wb_adr;
assign o_ddr3_wb_sel = s5i_wb_sel;
assign o_ddr3_wb_dat = s5i_wb_dat;
assign s5o_wb_ack = i_ddr3_wb_ack;
assign s5o_wb_dat = i_ddr3_wb_dat;

assign s7o_wb_ack = 0;
assign s7o_wb_dat = 0;

wishbone_arbiter #(
        .WB_DWIDTH(WB_DWIDTH),
        .WB_SWIDTH(WB_SWIDTH),
        .M0AWIDTH(WB_M0AWIDTH),
        .M1AWIDTH(WB_M1AWIDTH),
        .S0AWIDTH(WB_S0AWIDTH),
        .S1AWIDTH(WB_S1AWIDTH),
        .S2AWIDTH(WB_S2AWIDTH),
        .S3AWIDTH(WB_S3AWIDTH),
        .S4AWIDTH(WB_S4AWIDTH),
        .S5AWIDTH(WB_S5AWIDTH),
        .S6AWIDTH(WB_S6AWIDTH),
        .S7AWIDTH(WB_S7AWIDTH)
) u_wishbone_arbiter (
        .i_wb_clk               ( i_clk      ),

        // WISHBONE master 0 - Ethernet MAC
        .i_m0_wb_adr            ( m0o_wb_adr ),
        .i_m0_wb_sel            ( m0o_wb_sel ),
        .i_m0_wb_we             ( m0o_wb_we  ),
        .o_m0_wb_dat            ( m0i_wb_dat ),
        .i_m0_wb_dat            ( m0o_wb_dat ),
        .i_m0_wb_cyc            ( m0o_wb_cyc ),
        .i_m0_wb_stb            ( m0o_wb_stb ),
        .o_m0_wb_ack            ( m0i_wb_ack ),

        // WISHBONE master 1 - Processor
        .i_m1_wb_adr            ( m1o_wb_adr ),
        .i_m1_wb_sel            ( m1o_wb_sel ),
        .i_m1_wb_we             ( m1o_wb_we  ),
        .o_m1_wb_dat            ( m1i_wb_dat ),
        .i_m1_wb_dat            ( m1o_wb_dat ),
        .i_m1_wb_cyc            ( m1o_wb_cyc ),
        .i_m1_wb_stb            ( m1o_wb_stb ),
        .o_m1_wb_ack            ( m1i_wb_ack ),

        // WISHBONE slave 0 - RAM
        .o_s0_wb_adr            ( s0i_wb_adr ),
        .o_s0_wb_sel            ( s0i_wb_sel ),
        .o_s0_wb_we             ( s0i_wb_we  ),
        .i_s0_wb_dat            ( s0o_wb_dat ),
        .o_s0_wb_dat            ( s0i_wb_dat ),
        .o_s0_wb_cyc            ( s0i_wb_cyc ),
        .o_s0_wb_stb            ( s0i_wb_stb ),
        .i_s0_wb_ack            ( s0o_wb_ack ),

        // WISHBONE slave 1 - UART
        .o_s1_wb_adr            ( s1i_wb_adr ),
        .o_s1_wb_sel            ( s1i_wb_sel ),
        .o_s1_wb_we             ( s1i_wb_we  ),
        .i_s1_wb_dat            ( s1o_wb_dat ),
        .o_s1_wb_dat            ( s1i_wb_dat ),
        .o_s1_wb_cyc            ( s1i_wb_cyc ),
        .o_s1_wb_stb            ( s1i_wb_stb ),
        .i_s1_wb_ack            ( s1o_wb_ack ),

        // WISHBONE slave 2 - Timer
        .o_s2_wb_adr            ( s2i_wb_adr ),
        .o_s2_wb_sel            ( s2i_wb_sel ),
        .o_s2_wb_we             ( s2i_wb_we  ),
        .i_s2_wb_dat            ( s2o_wb_dat ),
        .o_s2_wb_dat            ( s2i_wb_dat ),
        .o_s2_wb_cyc            ( s2i_wb_cyc ),
        .o_s2_wb_stb            ( s2i_wb_stb ),
        .i_s2_wb_ack            ( s2o_wb_ack ),

        // WISHBONE slave 3 - Ethernet MAC
        .o_s3_wb_adr            ( s3i_wb_adr ),
        .o_s3_wb_sel            ( s3i_wb_sel ),
        .o_s3_wb_we             ( s3i_wb_we  ),
        .i_s3_wb_dat            ( s3o_wb_dat ),
        .o_s3_wb_dat            ( s3i_wb_dat ),
        .o_s3_wb_cyc            ( s3i_wb_cyc ),
        .o_s3_wb_stb            ( s3i_wb_stb ),
        .i_s3_wb_ack            ( s3o_wb_ack ),

        // WISHBONE slave 4 - Pushbuttons
        .o_s4_wb_adr            ( s4i_wb_adr ),
        .o_s4_wb_sel            ( s4i_wb_sel ),
        .o_s4_wb_we             ( s4i_wb_we  ),
        .i_s4_wb_dat            ( s4o_wb_dat ),
        .o_s4_wb_dat            ( s4i_wb_dat ),
        .o_s4_wb_cyc            ( s4i_wb_cyc ),
        .o_s4_wb_stb            ( s4i_wb_stb ),
        .i_s4_wb_ack            ( s4o_wb_ack ),

        // WISHBONE slave 5 - DDR3 SDRAM
        .o_s5_wb_adr            ( s5i_wb_adr ),
        .o_s5_wb_sel            ( s5i_wb_sel ),
        .o_s5_wb_we             ( s5i_wb_we  ),
        .i_s5_wb_dat            ( s5o_wb_dat ),
        .o_s5_wb_dat            ( s5i_wb_dat ),
        .o_s5_wb_cyc            ( s5i_wb_cyc ),
        .o_s5_wb_stb            ( s5i_wb_stb ),
        .i_s5_wb_ack            ( s5o_wb_ack ),

        // WISHBONE slave 6 - Simulation controller
        .o_s6_wb_adr            ( s6i_wb_adr ),
        .o_s6_wb_sel            ( s6i_wb_sel ),
        .o_s6_wb_we             ( s6i_wb_we  ),
        .i_s6_wb_dat            ( s6o_wb_dat ),
        .o_s6_wb_dat            ( s6i_wb_dat ),
        .o_s6_wb_cyc            ( s6i_wb_cyc ),
        .o_s6_wb_stb            ( s6i_wb_stb ),
        .i_s6_wb_ack            ( s6o_wb_ack ),

        // WISHBONE slave 7 - (unused)
        .o_s7_wb_adr            ( s7i_wb_adr ),
        .o_s7_wb_sel            ( s7i_wb_sel ),
        .o_s7_wb_we             ( s7i_wb_we  ),
        .i_s7_wb_dat            ( s7o_wb_dat ),
        .o_s7_wb_dat            ( s7i_wb_dat ),
        .o_s7_wb_cyc            ( s7i_wb_cyc ),
        .o_s7_wb_stb            ( s7i_wb_stb ),
        .i_s7_wb_ack            ( s7o_wb_ack )
);

/*ila_1 u_ila_1(
  .clk(i_clk),
  .probe0(cpu_instr_read), //  1 bit
  .probe1({cpu_pc, 2'b0}), // 32 bits
  .probe2(cpu_instr_ack),  //  1 bit
  .probe3(cpu_instr_data)  // 32 bits
);*/

`ifndef SYNTHESIS

wire            uart_monitor_data_ready;
wire    [7:0]   uart_out;
integer         uart_outfd;

reg [10:0] uart_monitor_cnt;

uart_monitor u_uart_monitor(
        .i_clk(i_clk),
        .i_rst(i_rst),
        .i_sample(!uart_monitor_cnt),
        .i_rx(o_uart_rx),
        .o_data_ready(uart_monitor_data_ready),
        .o_data(uart_out)
);

initial uart_outfd = $fopen("uart.out", "w");

always @(posedge i_clk) begin
  if (i_rst) begin
    uart_monitor_cnt <= 11'b0;
  end else begin
    uart_monitor_cnt <= uart_monitor_cnt ? uart_monitor_cnt-11'b1 : UART_CLK_DIV-1 ;
  end
  if (uart_monitor_data_ready) $fwrite(uart_outfd, "%c", uart_out);
end

reg trace;

initial trace = $test$plusargs("trace") != 0;

always @(posedge i_clk) begin
  if (trace) begin
    if (m1o_wb_cyc & m1o_wb_stb & m1o_wb_we & m1i_wb_ack) begin
      if (m1o_wb_sel[0]) $display("*%x=%x", {m1o_wb_adr,2'd0}, m1o_wb_dat[ 0+:8]);
      if (m1o_wb_sel[1]) $display("*%x=%x", {m1o_wb_adr,2'd1}, m1o_wb_dat[ 8+:8]);
      if (m1o_wb_sel[2]) $display("*%x=%x", {m1o_wb_adr,2'd2}, m1o_wb_dat[16+:8]);
      if (m1o_wb_sel[3]) $display("*%x=%x", {m1o_wb_adr,2'd3}, m1o_wb_dat[24+:8]);
    end
  end
end

`endif

endmodule
